JBoss Community Archive (Read Only)

GateIn Portal 3.5

JavaScript in GateIn

Modules in GateIn

GateIn Module Definition (GMD)

Declaring a module

We will first explain how a module can be integrated and consume dependencies, i.e other modules. A GateIn module consist in the declaration of a JavaScript self-executing function and its declaration in the gatein-resources.xml descriptor of the web application, the descriptor defines the module and its dependencies. At runtime GateIn will build a graph of resouces and their dependencies, when a client needs a particular module it will invoke a JS function that call RequireJS to resolve dependencies.

For instance if we have a foo module that uses the jQuery dependency:

foo.js
(function ($) {
  // Do something with jQuery
})(jQuery)

Our module is declared as a self-executing function in the foo.js file. This file is then declared in the gatein-resources.xml file:

gatein-resources.xml
...
<module>
  <name>foo</name>
  <script>
    <path>/foo.js</path>
  </script>
  <depends>
    <module>jquery</module>
    <as>jQuery</as>
  </depends>
</module>
...

The self-executing function declares

  • parameter for the function: $

  • arguments of the function invocation: jQuery

The self-executing function argument must match the dependencies

  • Function do no need to match XML dependencies order

  • Function can use parameter subset: an dependency can be declared in XML but not consumed as an argument

  • Parameters must be aliased in dependencies with the <as> tag XML

The self executing function argument is a JavaScript construct. The purpose of this construct is to pass arguments to the function and let the function name the arguments like they want and override the current scope of execution. For example jQuery uses it:

(function(window, undefined) { .... })(window);

Resources are related by the concept of dependency relationship that specifies how scripts are related to each other and how the modular system should be honored. In our example, the foo module needs to use jQuery, we say that they are in relationship: the module foo depends on jQuery. When the module is loaded, the jQuery module must be available to the module.

Translation to the AMD system

GateIn will translate into an AMD module:

define("SHARED/foo", ["SHARED/jquery"], function(jQuery) {
  return (function($) {
    // Do something with jQuery
  })(jQuery);
});
  • logical identifiers are translated to AMD identifiers

    • the foo module is translated to the SHARED/foo AMD module identifier, the SHARED prefix is added by GateIn for scoping the name, there are other existing scopes such as PORTLET and PORTAL

    • the dependency on the jquery module is translated to a ["SHARED/jquery"] AMD module dependency

  • the module function is wrapped two times

    • a first time by the GateIn module wrapper that delegates to the module function, the goal of this function is to provide a lazy evaluation of the module self-executing function.

    • a second time by the define AMD function that takes care of loading the module properly

  • the self-executing function module arguments must match the module dependencies expressed in the XML declaration

    • the jquery dependency is aliased to the jQuery name thanks to the XML as tag, as a consequence the GateIn function wrapper parameter is named jQuery

    • the module self-executing function argument is named jQuery to match the declared alias

    • the module self-executing function parameter is named $ and thus redefines the jquery depenendency to $ locally

At runtime the following happens:

  • the define function is invoked and declares the dependency

  • when the dependency is resolved (i.e the jquery module is loaded and available)

    • the module wrapper is invoked with the jQuery argument containing the jquery dependency

    • the module wrapper evaluates the self-executing function that resolves the jQuery argument to the jquery dependency

    • the self-executing function is invoked and the jquery dependency is available under the name $

Producing dependencies

In our example we have seen that a module is able to consume dependencies as arguments, now we need to explain how a module can produce a dependency. When a module self-executing function is evaluated it can return an object, this precise object is what the module itself. Let's modify our previous example to return a value:

foo.js
(function ($) {
  // Do something with jQuery
  return {hello:"world"}
})($)

In this example we return a simple JavaScript object, this object will be stored by the dependency system of AMD and provided as arguments of modules that want to consume the foo module. Pretty simple isn't it ?

Module scopes

We have seen previously that the name of a logical GateIn module translates into an AMD name with a prefix among SHARED, PORTLET or PORTAL. Thus a module is fully identified by its logical name and its scope. Scopes ensure that a module (and therefore the underlying web resource) is loaded at the right time when it is effectively required.

Shared scope

The shared scope does not related to a specific GateIn entity, instead a shared module should be consumed by other modules, it is declared in gatein-resources.xml with the top level module tag:

<module>
  <name>core</name>
  <script>
    <path>/core.js</path>
  </script>
  <depends>
    <module>base</module>
  </depends>
</module>

Portal scope

The module is related to a GateIn portal and should be loaded when the related portal is accessed:

<portal>
  <name>classic</name>
  <module>
    <script>
      <path>/classic.js</path>
    </script>
    <depends>
      <module>core</module>
    </depends>
  </module>
</portal>

Portlet scope

The module will be loaded when the corresponding portlet is accessed:

<portlet>
  <name>sitemap</name>
  <module>
    <script>
      <path>/sitemap.js</path>
    </script>
    <depends>
      <module>core</module>
    </depends>
  </module>
</portlet>

A module can only depend on a shared module, therefore any depends tag implicitly refers to a shared module.

Portlet dynamic module

As seen previous, portlet dependencies can be expressed in the gatein-resources.xml file, it forces to declare the portlet dependencies at packaging time when the corresponding war file is created.

The JSR286 specification provides a mechanism for modifying portal headers that can be used to load JavaScript files. Although this mechanism is portable, it has severe drawbacks:

  • A script can be loaded multiple times, specially if two portlets in two different war files load the same scripts since the only way to identify a script is by its URL

  • Scripts have to be loaded by the head section of the portal impacting front end performances

GateIn allows to provide the best of both worlds and create dynamic dependencies of a portlet at runtime, when the render phase of the portlet occurs:

public void render(RenderRequest req, RenderResponse resp) throws PortletException, IOException {
  resp.setProperty("org.gatein.javascript.dependency", "base");
  resp.addProperty("org.gatein.javascript.dependency", "common");
}

This code is equivalent to the following declaration:

<portlet>
  <name>MyPortlet</name>
  <module>
    <depends>
      <module>base</module>
    </depends>
    <depends>
      <module>common</module>
    </depends>
  </module>
</portlet>

Aliases

When a module is defined, the module name will be used in a JavaScript self-executing function argument:

<module>
  <name>foo</name>
  ...
</module>
<module>
  <name>bar</name>
  ...
  <depends>
    <module>foo</module>
  </depends>
</module>

The corresponding module foo code is:

(function(foo) {
})(foo)

Aliases allow to change this name and provide a different name for arguments. Aliasing is done thanks to the as XML tag:

Module alias

Other modules that depends on this will refer to it by the module alias defined in gatein-resources.xml.

<module>
  <name>foo</name>
  <as>Foo</as>
  ...
</module>
<module>
  <name>bar</name>
  ...
  <depends>
    <module>foo</module>
  </depends>
</module>

The corresponding module foo code is:

(function(Foo) {
})(Foo)

Dependency alias

The same as tag can be used in the depends tags providing a local alias:

<module>
  <name>foo</name>
  ...
</module>
<module>
  <name>bar</name>
  ...
  <depends>
    <module>foo</module>
    <as>Foo</as>
  </depends>
</module>

Custom adapters

JavaScript does not have a standard module until Ecmascript 6, several proposal exist for defining modules all revolving around the same module pattern. GateIn decided to use the AMD format because it is really adapted to the web and its asynchronous nature.

As consequence sometimes you can have to deal with included script that do not match the self-executing function declaration format or requirejs format expected by GateIn and is RequireJS integration. Custom code is required for adapting the script to the expected format. To provide this bit of flexibility it is possible to declare an adapter that will wrap the adapted script.

The jQuery library is a good example of how a custom adapter is useful, thanks to the adapter we can reuse the jQuery without any change, easing the integration and maintenance of this library in GateIn. jQuery uses the following construct for defining itself:

(function(window, undefined) {
})(window);

The main issue with this construct is that it will bind jQuery to the window but most importantly it will not return any value as expected by the dependency system. Thanks to the custom adapter we can integrate it trivially:

<module>
 <name>jquery</name>
 <script>
   <adapter>
     (function() {
       <include>/jquery.js</include>
       return jQuery.noConflict(true);
     })();
   </adapter>
 </script>
</module>

The adapter tag can contains mixed content and the include tag will perform a mere inclusion (as C language includes) of the original jQuery script in the resulting module:

define("SHARED/jquery", [], function() {
  return (function() {
    (function(window, undefined) {
    })(window);
   return jQuery.noConflict(true);
  })();
});

Module resources

The dependency notion is quite trivial, until now we have covered dependencies between modules. However AMD allows to define dependencies onto a resource loaded by a module based on the loader plugin.

Loader plugin are interesting because they can load resources thanks to the AMD loading mechanism and benefit from the same performances and parallel loading.

There are some useful loader plugin:

  • The text plugin for loading text resources such as a stylesheet or a template

  • The i18n plugin for internationalized bundle support

Using configuration in GateIn for AMD loader plugin’s target resource is straightforward. For example, we want to build some HTML by Javascript, the text.js AMD loader plugin can help with this issue. The plugin will load the template (plugin’s resource) and pass it into your module definition callback as a parameter.

First, we need to declare text as a GateIn module, it was written as a native module that depend on a predefined module of RequireJS module. Thanks to the native support mechanism, this configuration works transparently without modifying the 3rd party library.

<module>
  <name>text</name>
  <script>
    <path>/requirejs/js/plugins/text.js</path>
  </script>
  <depends>
    <!-- here module means RequireJS itself -->
    <module>module</module>
  </depends>
</module>

Now let's define our foo module that need the text plugin to load the template via the resource XML tag:

<module>
  <name>foo</name>
  ...
  <depends>
    <module>text</module>
    <as>myTemplate</as>
    <resource>/path/to/template.tmpl</resource>
  </depends>
</module>

Finally, the foo module will have its template loaded by the text plugin:

(function(myTemplate) {
  //parse the 'myTemplate' then append to DOM
})(myTemplate);

Load groups

Until now we focused on the logical definition of modules, i.e as a self-executing function and an XML descriptor. When those modules loaded by the browser, GateIn serves them as web resource. By default a module will be served as a single resource, however this can be an issue in the production system and the load group feature allows to decouple the logical module and the JavaScript resource serving:

  • The granularity of the module system should have a small impact on the performance, specially when there are many small modules

  • The front end performance should have a small impact on how the JavaScript modularity

The load-group XML tag can be used to group modules together in the same web resources. When a module of a load-group is requested, the web resource containing the modules of the load group is loaded, for instance if we have the foo and bar modules grouped by foobar :

<module>
  <name>foo</name>
  <load-group>foobar</load-group>
  ...
</module>
<module>
  <name>bar</name>
  <load-group>foobar</load-group>
  ...
</module>

When the foo is loaded the AMD loader will load the foobar group instead of only foo, consequently the bar module is also available now. If there is any request for bar module, no more request is made as it is already loaded.

Note: Don't use group name that follow by dash and 2 character, for example: forbar-as. This may cause unexpected parsing group name on the server (on gatein 3.5.0.Final)

Localisation

GateIn can localise modules on the server: the JavaScript of each module is rewritten and replace keys by values borrowed from resource bundles. Each script will be available under a different URL for each localisation to provide maximum caching of the resource.

The localisation of a script is activated with the resource-bundle XML tag along with the path tag. Resources bundles are declared by the supported-locale tag and must be available in the web application classpath:

<module>
  <name>greetings</name>
  <supported-locale>de</supported-locale>
  <supported-locale>en</supported-locale>
  <supported-locale>fr</supported-locale>
  <supported-locale>vi</supported-locale>
  <supported-locale>ru</supported-locale>
  <script>
    <path>/path/to/greetings.js</path>
    <resource-bundle>greetings_bundle</resource-bundle>
  </script>
  ...
</module>

GateIn will escape any key of the form ${key} and will look up the value in the resource bundles. For instance one can build easily a JSON object containing localised values:

greetings.js
(function() {
  return {
    "hello": "${hello}",
    "goodbye": "${goodbye}"
  };
})();

At runtime different versions of this script will be served by GateIn with the properly escaped values.

Scripts in GateIn

Whenever possible, JavaScript should be written as AMD module (remember that GateIn also support custom adapter mechanism to adapt traditional script into GateIn module). This is the best practice, it helps to manage dependencies, organize code, not pollute the global scope and do parallel loading.

However we do acknowledge that in some specific case, people may want to use the old way: load the scripts as non-AMD module, GateIn still support this, the good thing is that we can still manage those js dependencies:

<scripts>
  <name>foo</name>
  <script>
    <path>/path/to/foo.js</path>
  </script>
  <depends>
    <scripts>bar</scripts>
  <depends>
</scripts>
  • Traditional script must be declared in scripts tag instead of module

  • Dependencies can be declared using depends but it can only depends on other scripts, not modules. Tthis also true at module side, a module can only depends on other modules

  • Javascript declared in scripts will be push on the html head, that means those scripts will be loaded and executed before the DOM is created and will likely have an impact on front end performances

We can also use a script provided on the internet by using the url tag instead of the script tag:

<scripts>
  <name>foo</name>
  <url>http://path.to/foo.js</url>
</scripts>
JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-10 12:48:43 UTC, last content change 2013-05-06 02:55:53 UTC.